Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | import { notFound } from 'next/navigation' import { canPerformAction } from '@/lib/classroom/access-control' import { getAllSkillMastery, getPlayerCurriculum, getPracticeStudent, getRecentSessions, getRecentSessionResults, } from '@/lib/curriculum/server' import { getSessionMode } from '@/lib/curriculum/session-mode' import { getSessionModeComfortLevel } from '@/lib/curriculum/session-mode-comfort' import { getActiveSessionPlan } from '@/lib/curriculum/session-planner' import { getUserId } from '@/lib/viewer' import { DashboardClient } from './DashboardClient' // Disable caching for this page - progress data should be fresh export const dynamic = 'force-dynamic' interface DashboardPageProps { params: Promise<{ studentId: string }> searchParams: Promise<{ tab?: string }> } /** * Dashboard Page - Server Component * * Shows the student's tabbed dashboard with: * - Overview tab: Current level, progress, session controls * - Skills tab: Detailed skill mastery, BKT analysis, skill management * - History tab: Past sessions (future) * * This page is always accessible regardless of session state. * Parents/teachers can view stats even while a session is in progress. * * URL: /practice/[studentId]/dashboard?tab=overview|skills|history */ export default async function DashboardPage({ params, searchParams }: DashboardPageProps) { const { studentId } = await params const { tab } = await searchParams // Get database user ID for authorization and socket notifications const userId = await getUserId() // Fetch player data in parallel (includes session mode to avoid client-side waterfall) const [player, curriculum, skills, recentSessions, activeSession, problemHistory, sessionMode] = await Promise.all([ getPracticeStudent(studentId), getPlayerCurriculum(studentId), getAllSkillMastery(studentId), getRecentSessions(studentId, 200), getActiveSessionPlan(studentId), getRecentSessionResults(studentId, 2000), // For Skills tab BKT analysis getSessionMode(studentId), ]) // 404 if player doesn't exist if (!player) { notFound() } // Check authorization - user must have view access to this player const hasAccess = await canPerformAction(userId, studentId, 'view') if (!hasAccess) { notFound() // Return 404 to avoid leaking existence of player } // Get skill IDs that are in the student's active practice rotation // practiceLevel !== 'none' means the skill is enabled for practice, NOT that it's mastered const currentPracticingSkillIds = skills.filter((s) => s.isPracticing).map((s) => s.skillId) // Compute comfort level (depends on sessionMode, so must be after Promise.all) const comfortResult = await getSessionModeComfortLevel(studentId, sessionMode) return ( <DashboardClient studentId={studentId} player={player} curriculum={curriculum} skills={skills} recentSessions={recentSessions} activeSession={activeSession} currentPracticingSkillIds={currentPracticingSkillIds} problemHistory={problemHistory} initialTab={ tab === 'overview' || tab === 'skills' || tab === 'history' || tab === 'scoreboard' || tab === 'notes' || tab === 'observers' || tab === 'relationships' ? (tab as | 'overview' | 'skills' | 'history' | 'scoreboard' | 'notes' | 'observers' | 'relationships') : undefined } userId={userId} initialSessionMode={{ sessionMode, comfortLevel: comfortResult.overall, comfortByMode: comfortResult.byMode, }} /> ) } |